package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/olivere/elastic/v7"
	"github.com/opentracing/opentracing-go"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"shop_srvs/goods_srv/global"
	"shop_srvs/goods_srv/model"
	"shop_srvs/goods_srv/proto"
)

type GoodsServer struct {
	*proto.UnimplementedGoodsServer
}

func ModelToResponse(goods model.Goods) proto.GoodsInfoResponse {
	return proto.GoodsInfoResponse{
		Id:              goods.ID,
		CategoryId:      goods.CategoryID,
		BrandId:         goods.BrandsID,
		Name:            goods.Name,
		GoodsSn:         goods.GoodsSn,
		ClickNum:        goods.ClickNum,
		SoldNum:         goods.SoldNum,
		FavNum:          goods.FavNum,
		MarketPrice:     goods.MarketPrice,
		ShopPrice:       goods.ShopPrice,
		GoodsBrief:      goods.GoodsBrief,
		ShipFree:        goods.ShipFree,
		GoodsFrontImage: goods.GoodsFrontImages,
		IsNew:           goods.IsNew,
		IsHot:           goods.IsHot,
		OnSale:          goods.OnSale,
		DescImages:      goods.DescImages,
		Images:          goods.Images,
		Category: &proto.CategoryBriefInfoResponse{
			Id:   goods.Category.ID,
			Name: goods.Category.Name,
		},
		Brand: &proto.BrandInfoResponse{
			Id:   goods.Brands.ID,
			Name: goods.Brands.Name,
			Logo: goods.Brands.Logo,
		},
	}
}
func (g *GoodsServer) GoodsList1(ctx context.Context, req *proto.GoodsFilterRequest) (*proto.GoodsListResponse, error) {
	//关键词搜索，查询新品，查询热门商品，通过价格区间筛选，通过商品分类筛选
	var goods []model.Goods

	// 不要修改全局的DB，而是使用局部的localDB
	localDB := global.DB.Model(model.Goods{})
	// 拼接查询语句
	if req.KeyWords != "" {
		localDB = localDB.Where("name LIKE ?", "%"+req.KeyWords+"%")
	}
	if req.IsHot != false {
		localDB = localDB.Where(model.Goods{IsHot: true})
	}
	if req.IsNew != false {
		localDB = localDB.Where(model.Goods{IsNew: true})
	}
	if req.PriceMax > 0 {
		localDB = localDB.Where("shop_price<=?", req.PriceMax)
	}
	if req.PriceMin > 0 {
		localDB = localDB.Where("shop_price>=?", req.PriceMin)
	}
	if req.Brand > 0 {
		localDB = localDB.Where(model.Goods{BrandsID: req.Brand})
	}

	//通过catrgory查询商品
	//子查询 嵌套 子查询
	//一级：SELECT id FROM `category` where id=130358;
	//二级：SELECT id FROM `category` where parent_category_id=130358;
	//三级：SELECT id FROM `category` where parent_category_id in (SELECT id FROM `category` where parent_category_id=130358);
	var subQuery string
	if req.TopCategory > 0 { //顶级Categoryid
		var category model.Category
		if res := global.DB.First(&category, req.TopCategory); res.RowsAffected == 0 {
			return nil, status.Errorf(codes.NotFound, "商品分类不存在")
		}
		if category.Level == 1 {
			subQuery = fmt.Sprintf("select id from category WHERE parent_category_id "+
				"in (select id from category WHERE parent_category_id=%d)", req.TopCategory)
		} else if category.Level == 2 {
			subQuery = fmt.Sprintf("select id from category WHERE parent_category_id=%d", req.TopCategory)
		} else if category.Level == 3 {
			subQuery = fmt.Sprintf("select id from category WHERE id=%d", req.TopCategory)
		}
		localDB = localDB.Where(fmt.Sprintf("category_id in (%s)", subQuery))
	}
	var count int64
	localDB.Count(&count) //这个total的总数量一定要在分页之前完成，否则就是分页的数量了

	if res := localDB.Preload("Category").Preload("Brands").Scopes(Paginate(int(req.Pages),
		int(req.PagePerNums))).Find(&goods); res.Error != nil {
		return nil, res.Error
	}
	var data []*proto.GoodsInfoResponse
	for _, good := range goods {
		goodsInfoResponse := ModelToResponse(good)
		data = append(data, &goodsInfoResponse)
	}

	return &proto.GoodsListResponse{
		Total: int32(count),
		Data:  data,
	}, nil
}

type Result struct {
	ID int32 `json:"id"`
}

func (g *GoodsServer) GoodsList(ctx context.Context, req *proto.GoodsFilterRequest) (*proto.GoodsListResponse, error) {
	parentSpan := opentracing.SpanFromContext(ctx)
	//关键词搜索，查询新品，查询热门商品，通过价格区间筛选，通过商品分类筛选
	/*
		1.es查询出所有id(mysql找子id列表)
		2.根据id查询mysql表具体商品
	*/

	//match bool 复合查询
	q := elastic.NewBoolQuery()
	// 在名字，简介里面查找关键字
	if req.KeyWords != "" {
		q = q.Must(elastic.NewMultiMatchQuery(req.KeyWords, "name", "goods_brief"))
	}
	if req.IsHot != false {
		q = q.Filter(elastic.NewTermQuery("is_hot", req.IsHot))
	}
	if req.IsNew != false {
		q = q.Filter(elastic.NewTermQuery("is_new", req.IsNew))
	}
	if req.PriceMax > 0 {
		q = q.Filter(elastic.NewRangeQuery("shop_price").Lte(req.PriceMax))
	}
	if req.PriceMin > 0 {
		q = q.Filter(elastic.NewRangeQuery("shop_price").Gte(req.PriceMin))
	}
	if req.Brand > 0 {
		q = q.Filter(elastic.NewTermQuery("brands_id", req.Brand))
	}

	//通过catrgory查询商品
	//子查询 嵌套 子查询
	//一级：SELECT id FROM `category` where id=130358;
	//二级：SELECT id FROM `category` where parent_category_id=130358;
	//三级：SELECT id FROM `category` where parent_category_id in (SELECT id FROM `category` where parent_category_id=130358);
	var subQuery string
	if req.TopCategory > 0 { //顶级Categoryid
		var category model.Category
		if res := global.DB.First(&category, req.TopCategory); res.RowsAffected == 0 {
			return nil, status.Errorf(codes.NotFound, "商品分类不存在")
		}
		if category.Level == 1 {
			subQuery = fmt.Sprintf("select id from category WHERE parent_category_id "+
				"in (select id from category WHERE parent_category_id=%d)", req.TopCategory)
		} else if category.Level == 2 {
			subQuery = fmt.Sprintf("select id from category WHERE parent_category_id=%d", req.TopCategory)
		} else if category.Level == 3 {
			subQuery = fmt.Sprintf("select id from category WHERE id=%d", req.TopCategory)
		}

		var results []Result
		var categoryIds []interface{}
		//原生sql，找到subQuery这些id
		global.DB.Model(model.Category{}).Raw(subQuery).Scan(&results)
		for _, result := range results {
			categoryIds = append(categoryIds, result.ID)
		}

		//执行过滤，生成terms多查询
		q = q.Filter(elastic.NewTermsQuery("category_id", categoryIds...))
	}

	//分页
	switch {
	case req.PagePerNums > 100:
		req.PagePerNums = 100
	case req.PagePerNums <= 0:
		req.PagePerNums = 10
	}

	elasticSpan := opentracing.GlobalTracer().StartSpan("es_good_list",
		opentracing.ChildOf(parentSpan.Context()))
	result, err := global.EsClient.Search().Index(model.EsGoods{}.GetIndexName()).Query(q).From(int(req.Pages)).
		Size(int(req.PagePerNums)).Do(ctx)
	elasticSpan.Finish()

	if err != nil {
		return nil, err
	}

	if result.Hits.TotalHits.Value == 0 {
		return &proto.GoodsListResponse{}, nil
	}

	goodsIds := make([]int32, 0)
	for _, hit := range result.Hits.Hits {
		goods := model.EsGoods{}
		_ = json.Unmarshal(hit.Source, &goods)
		goodsIds = append(goodsIds, goods.ID)
	}

	//根据es给出的id，去mysql联表查询
	var data []*proto.GoodsInfoResponse
	var goods []model.Goods
	if len(goodsIds) != 0 {
		goodsListSpan := opentracing.GlobalTracer().StartSpan("find_good_list",
			opentracing.ChildOf(parentSpan.Context()))
		re := global.DB.Preload("Category").Preload("Brands").Find(&goods, goodsIds)
		goodsListSpan.Finish()
		if re.Error != nil {
			return nil, re.Error
		}
		for _, good := range goods {
			goodsInfoResponse := ModelToResponse(good)
			data = append(data, &goodsInfoResponse)
		}
	}

	//zap.S().Info("elasticsearch data:", data)
	return &proto.GoodsListResponse{
		Total: int32(result.Hits.TotalHits.Value), //这个total的总数量一定要在分页之前完成，否则就是分页的数量了
		Data:  data,
	}, nil
}

// BatchGetGoods 现在用户提交订单有多个商品，你得批量查询商品的信息吧
func (g *GoodsServer) BatchGetGoods(ctx context.Context, req *proto.BatchGoodsIdInfo) (*proto.GoodsListResponse, error) {
	var goods []model.Goods
	var data []*proto.GoodsInfoResponse
	result := global.DB.Where(req.Id).Find(&goods)
	for _, good := range goods {
		goodsInfoResponse := ModelToResponse(good)
		data = append(data, &goodsInfoResponse)
	}

	return &proto.GoodsListResponse{
		Total: int32(result.RowsAffected),
		Data:  data,
	}, nil
}
func (g *GoodsServer) GetGoodsDetail(ctx context.Context, req *proto.GoodInfoRequest) (*proto.GoodsInfoResponse, error) {
	var goods model.Goods
	if res := global.DB.Preload("Category").Preload("Brands").First(&goods, req.Id); res.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "商品不存在")
	}
	goodsInfoResponse := ModelToResponse(goods)
	return &goodsInfoResponse, nil
}
func (g *GoodsServer) CreateGoods(ctx context.Context, req *proto.CreateGoodsInfo) (*proto.GoodsInfoResponse, error) {
	var category model.Category
	if res := global.DB.First(&category, req.CategoryId); res.RowsAffected == 0 {
		return nil, status.Errorf(codes.InvalidArgument, "商品分类不存在")
	}

	var brand model.Brands
	if res := global.DB.First(&brand, req.BrandId); res.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "品牌不存在")
	}

	//这里没有看到图片文件是如何上传的
	goods := &model.Goods{
		Brands:      brand,
		BrandsID:    req.BrandId,
		Category:    category,
		CategoryID:  category.ID,
		GoodsSn:     req.GoodsSn,
		MarketPrice: req.MarketPrice,
		ShopPrice:   req.ShopPrice,
		GoodsBrief:  req.GoodsBrief,
		ShipFree:    req.ShipFree,
		//Images: req.Images,
		//DescImages: req.DescImages,
		GoodsFrontImages: req.GoodsFrontImage,
		IsNew:            req.IsNew,
		IsHot:            req.IsHot,
		OnSale:           req.OnSale,
	}
	tx := global.DB.Begin()
	if res := tx.Create(&goods); res.RowsAffected == 0 { //create的时候会自动调用钩子函数，所以在这里添加事务处理
		tx.Rollback()
		return nil, res.Error
	}
	tx.Commit()
	return &proto.GoodsInfoResponse{
		Id: goods.ID,
	}, nil
}
func (g *GoodsServer) DeleteGoods(ctx context.Context, req *proto.DeleteGoodsInfo) (*emptypb.Empty, error) {
	if result := global.DB.Delete(&model.Goods{BaseModel: model.BaseModel{ID: req.Id}}, req.Id); result.Error != nil {
		return nil, status.Errorf(codes.NotFound, "商品不存在")
	}
	return &emptypb.Empty{}, nil
}
func (g *GoodsServer) UpdateGoods(ctx context.Context, req *proto.CreateGoodsInfo) (*emptypb.Empty, error) {
	var goods model.Goods
	if res := global.DB.First(&goods, req.Id); res.RowsAffected == 0 {
		return nil, status.Errorf(codes.NotFound, "商品不存在")
	}

	var category model.Category
	if res := global.DB.First(&category, req.CategoryId); res.RowsAffected == 0 {
		return nil, status.Errorf(codes.InvalidArgument, "商品分类不存在")
	}

	var brand model.Brands
	if res := global.DB.First(&brand, req.BrandId); res.RowsAffected == 0 {
		return nil, status.Errorf(codes.InvalidArgument, "品牌不存在")
	}

	goods = model.Goods{
		BaseModel: model.BaseModel{
			ID: req.Id,
		},
		Name:             req.Name,
		Brands:           brand,
		BrandsID:         req.BrandId,
		Category:         category,
		CategoryID:       category.ID,
		GoodsSn:          req.GoodsSn,
		MarketPrice:      req.MarketPrice,
		ShopPrice:        req.ShopPrice,
		GoodsBrief:       req.GoodsBrief,
		ShipFree:         req.ShipFree,
		Images:           req.Images,
		DescImages:       req.DescImages,
		GoodsFrontImages: req.GoodsFrontImage,
		IsNew:            req.IsNew,
		IsHot:            req.IsHot,
		OnSale:           req.OnSale,
		Stocks:           req.Stocks,
		SoldNum:          req.SoldNum,
		FavNum:           req.FavNum,
		ClickNum:         req.ClickNum,
	}

	tx := global.DB.Begin()
	if res := tx.Model(&model.Goods{}).Where("id=?", req.Id).Updates(goods); res.RowsAffected == 0 { //调用钩子函数
		tx.Rollback()
		return nil, res.Error
	}
	tx.Commit()
	return &emptypb.Empty{}, nil
}
